﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

using LLVMSharp;

namespace ConsoleApp
{
    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int Add(int a, int b);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate int Mul(int a, int b);

    public class Program
    {
        public static void Main(string[] args)
        {

            LLVMBool Success = false;

            LLVMModuleRef mod = LLVM.ModuleCreateWithName("LLVMSharpIntro");
            LLVMTypeRef[] param_types = { LLVM.Int32Type(), LLVM.Int32Type() ,LLVM.Int32Type()};
            LLVMTypeRef ret_type = LLVM.FunctionType(LLVM.Int32Type(), param_types, true);

            LLVMValueRef sum = LLVM.AddFunction(mod, "getMul", ret_type);//getMul

            LLVMBasicBlockRef entry = LLVM.AppendBasicBlock(sum, "entry_mul_a_and_b");
            LLVMBuilderRef builder = LLVM.CreateBuilder();

            LLVM.PositionBuilderAtEnd(builder, entry);
            LLVMValueRef tmp = LLVM.BuildAdd(builder, LLVM.GetParam(sum, 0), LLVM.GetParam(sum, 1), "tmp_resout");
            LLVM.BuildRet(builder, tmp);

            if (LLVM.VerifyModule(mod, LLVMVerifierFailureAction.LLVMPrintMessageAction, out string error) != Success)
            {
                Console.WriteLine($"Error: {error}");
            }

            LLVM.LinkInMCJIT();
            
            LLVM.InitializeX86TargetMC();
            LLVM.InitializeX86Target();
            LLVM.InitializeX86TargetInfo();
            LLVM.InitializeX86AsmParser();
            LLVM.InitializeX86AsmPrinter();

            //LLVM.InitializeAMDGPUTargetMC();
            //LLVM.InitializeAMDGPUTarget();
            //LLVM.InitializeAMDGPUTargetInfo();
            //LLVM.InitializeAMDGPUAsmParser();
            //LLVM.InitializeAMDGPUAsmPrinter();
            
            //LLVM.InitializePowerPCAsmParser

            LLVMMCJITCompilerOptions options = new LLVMMCJITCompilerOptions { NoFramePointerElim = 1 };
            LLVM.InitializeMCJITCompilerOptions(options);
            if (LLVM.CreateMCJITCompilerForModule(out var engine, mod, options, out error) != Success)
            {
                Console.WriteLine($"Error: {error}");
            }

            var addMethod = (Add)Marshal.GetDelegateForFunctionPointer(LLVM.GetPointerToGlobal(engine, sum), typeof(Add));



            int result = addMethod(5 ,7);

            Console.WriteLine("Result of Mul is: " + result);

            if (LLVM.WriteBitcodeToFile(mod, "Mul.bc") != 0)
            {
                Console.WriteLine("error writing bitcode to file, skipping");
            }
            
            
            LLVM.DumpModule(mod);
            LLVM.DisposeBuilder(builder);
            LLVM.DisposeExecutionEngine(engine);

            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine();
            }
            Console.ReadLine();
        }
    }
}
